from django import forms
from web.models import UserInfo
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.conf import settings
import random
from utils.tecent.sms import send_sms_single
from django_redis import get_redis_connection
from utils import encrypt
from .bootstrap import BootStrapForm
from web.models import UserInfo


class RegisterModelForm(BootStrapForm, forms.ModelForm):
    # 表单展示
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    password = forms.CharField(
        label='密码',
        # 限制密码长短
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '密码长度不能小于8',
            'max_length': '密码长度不能大于64',
        },
        widget=forms.PasswordInput())

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())
    confirm_password = forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '重复密码长度不能小于8',
            'max_length': '重复密码长度不能大于64',
        },
        widget=forms.PasswordInput())

    class Meta:
        model = UserInfo
        fields = ['username', 'password', 'confirm_password', 'email', 'mobile_phone', 'code']

    # 验证数据
    def clean_username(self):
        '''username的钩子'''
        username = self.cleaned_data['username']
        exists = UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError("用户名已经存在")
        return username

    def clean_email(self):
        '''email的钩子'''
        email = self.cleaned_data['email']
        exists = UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError("邮箱已经被注册")
        return email

    def clean_password(self):
        '''密码的钩子'''
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    def clean_confirm_password(self):
        '''重复密码的钩子'''
        pwd = self.cleaned_data.get('password')
        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])
        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')
        return confirm_pwd

    def clean_mobile_phone(self):
        '''手机号的钩子'''
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('手机号已经被注册')
        return mobile_phone

    def clean_code(self):
        '''验证码的钩子'''
        # redis中的key是mobile_phone,val是code所以要根据手机号去拿到验证码就避免了验证码匹配别的手机
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code
        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或者未发送，请重新发送。')
        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code.strip():  # .strip()去除字符串空格
            raise ValidationError('验证码错误，请重新输入')
        return code


class SendSmsForm(forms.Form):
    """验证手机"""
    # ②在此先获得手机号用正则匹配验证手机号格式是否正确
    mobile_phone = forms.CharField(label="手机号", validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    # ③在此重写init方法调用父类form.Form的init方法，因为父类的init方法中有request对象
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # ④在此接受request对象
        self.request = request

    def clean_mobile_phone(self):
        """定义一个钩子函数对view.account中传过来的数据进行验证"""
        # ⑤在此获得手机号
        mobile_phone = self.cleaned_data["mobile_phone"]

        # ⑥判断短信模板是否有问题(虽然tpl_id写在settings的设置中并且是一个字典，但是为了防止爬虫等还是要验证一下)
        tpl = self.request.GET.get('tpl')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not template_id:
            # 抛出ValidationError的时候，底层会自动调用self.add_error('mobile_phone','短信模板多错误')
            # 等同于将错误的errmsg添加到了form.errors中,所以才会在前段不同的span标签中看到errmsg
            raise ValidationError("短信模板错误")
        exists = UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        # ⑦根据请求方式验证数据库中是否有手机号了
        if tpl == 'login':
            if not exists:
                raise ValidationError('手机号不存在')
        else:
            if exists:
                raise ValidationError('手机号已经存在')

        # ⑧发短信
        # 生成四位随机数字
        # code = 520
        code = random.randrange(1000, 9999)
        # 调用utils的发短信工具,[code,]是参数列表
        sms = send_sms_single(mobile_phone, template_id, [code, ])
        # 判断
        if sms['result'] != 0:
            raise ValidationError("短信发送失败，{0}".format(sms['errmsg']))

        # ⑨将短信写入redis(django-redis)
        conn = get_redis_connection("default")
        conn.set(mobile_phone, code, ex=60)  # ex为超时时间
        return mobile_phone


class LoginSmsForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())

    def clean_mobile_phone(self):
        '''手机钩子'''
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if not exists:
            raise ValidationError('手机号不存在')
        return mobile_phone


class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label='邮箱或手机号')
    password = forms.CharField(label='密码', widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='图片验证码')

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    def clean_code(self):
        """ 钩子 图片验证码是否正确？ """
        # 读取用户输入的yanzhengm
        code = self.cleaned_data['code']

        # 去session获取自己的验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期，请重新获取')

        if code.strip().upper() != session_code.strip().upper():
            raise ValidationError('验证码输入错误')

        return code
